package com.fastjars.freemarker;

import freemarker.core.Environment;
import freemarker.template.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Writer;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * AbstractTag
 *
 * @author Brant Liu <br> 邮箱：<br>lbf1988@qq.com <br>日期：<br>2017/12/31
 * @version 1.0.0
 */
public abstract class AbstractTag extends WrappingTemplateModel implements TemplateDirectiveModel {
    public static final Logger LOGGER = LoggerFactory.getLogger(AbstractTag.class);

    private Environment environment;
    private Map<?, ?> params;
    private TemplateModel[] templateModels;
    private TemplateDirectiveBody templateDirectiveBody;

    @Override
    public void execute(Environment environment, Map map, TemplateModel[] templateModels, TemplateDirectiveBody templateDirectiveBody) throws TemplateException, IOException {
        this.environment = environment;
        this.params = map;
        this.templateModels = templateModels;
        this.templateDirectiveBody = templateDirectiveBody;
        this.onRender();
    }

    public abstract void onRender();

    /**
     * 设置属性
     * @param key
     * @param value
     */
    protected void setVariable(String key, Object value) {
        try {
            environment.setVariable(key, super.getDefaultObjectWrapper().wrap(value));
        } catch (TemplateModelException e) {
            LOGGER.error("setVariable(String key,Object value) is error!", e);
        }
    }

    /**
     * 渲染Text
     * @param text
     */
    protected void renderText(String text) {
        try {
            environment.getOut().write(text == null ? "null" : text);
        } catch (IOException e) {
            LOGGER.error("AbstractTag renderText error", e);
        }
    }

    /**
     * 渲染主体
     */
    protected void renderBody() {
        try {
            templateDirectiveBody.render(environment.getOut());
        } catch (TemplateException e) {
            LOGGER.error("AbstractTag renderBody is error!", e);
        } catch (IOException e) {
            LOGGER.error("AbstractTag renderBody is error!", e);
        }
    }

    /**
     * 渲染主体
     * @param writer
     */
    protected void renderBody(Writer writer) {
        try {
            templateDirectiveBody.render(writer);
        } catch (TemplateException e) {
            LOGGER.error("AbstractTag renderBody(Writer writer) is error!", e);
        } catch (IOException e) {
            LOGGER.error("AbstractTag renderBody(Writer writer) is error!", e);
        }
    }

    protected TemplateModel[] getTemplateModels() {
        return templateModels;
    }

    protected TemplateDirectiveBody getTemplateDirectiveBody() {
        return templateDirectiveBody;
    }

    protected Writer getWriter(){
        return this.environment.getOut();
    }

    /**
     * 获取参数...................................................
     */
    public String getParam(String key) {
        TemplateModel model = (TemplateModel) params.get(key);
        if (model == null) {
            return null;
        }
        try {
            if (model instanceof TemplateScalarModel) {
                return ((TemplateScalarModel) model).getAsString();
            }
            if ((model instanceof TemplateNumberModel)) {
                return ((TemplateNumberModel) model).getAsNumber().toString();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    public String getParam(String key, String defaultValue) {
        String value = getParam(key);
        if (value != null)
            return value;
        return defaultValue;
    }

    public Long getParamToLong(String key) {
        TemplateModel model = (TemplateModel) params.get(key);
        if (model == null) {
            return null;
        }
        try {
            if (model instanceof TemplateNumberModel) {
                return ((TemplateNumberModel) model).getAsNumber().longValue();
            }
            if (model instanceof TemplateScalarModel) {
                String string = ((TemplateScalarModel) model).getAsString();
                if (null == string || "".equals(string.trim())) {
                    return null;
                }
                return Long.parseLong(string);
            }
        } catch (Exception e) {
            throw new RuntimeException("must number!", e);
        }
        return null;
    }

    public Long getParamToLong(String key, long defaultValue) {
        Long value = getParamToLong(key);
        if (value != null)
            return value;
        return defaultValue;
    }

    public Byte getParamToByte(String key, byte defaultValue) {
        String value = getParam(key);
        if (value != null)
            return Byte.parseByte(value);
        return defaultValue;
    }

    public BigInteger getParamToBigInteger(String key) {
        TemplateModel model = (TemplateModel) params.get(key);
        if (model == null) {
            return null;
        }
        try {
            if (model instanceof TemplateNumberModel) {
                long number = ((TemplateNumberModel) model).getAsNumber().longValue();
                return BigInteger.valueOf(number);
            }
            if (model instanceof TemplateScalarModel) {
                String string = ((TemplateScalarModel) model).getAsString();
                if (null == string || "".equals(string.trim())) {
                    return null;
                }
                return new BigInteger(string);
            }
        } catch (Exception e) {
            throw new RuntimeException("must number!", e);
        }
        return null;
    }

    public BigInteger getParamToBigInteger(String key, BigInteger defaultValue) {
        BigInteger value = getParamToBigInteger(key);
        if (value != null)
            return value;
        return defaultValue;
    }

    public Integer getParamToInt(String key) {
        TemplateModel model = (TemplateModel) params.get(key);
        if (model == null) {
            return null;
        }
        try {
            if (model instanceof TemplateNumberModel) {
                return ((TemplateNumberModel) model).getAsNumber().intValue();
            }
            if (model instanceof TemplateScalarModel) {
                String string = ((TemplateScalarModel) model).getAsString();
                if (null == string || "".equals(string.trim())) {
                    return null;
                }
                return Integer.parseInt(string);
            }
        } catch (Exception e) {
            throw new RuntimeException("must number!", e);
        }
        return null;
    }

    public Integer getParamToInt(String key, Integer defaultValue) {
        Integer value = getParamToInt(key);
        if (null != value)
            return value;
        return defaultValue;
    }

    public Integer[] getParamToIntArray(String key) {
        String string = getParam(key);
        if (null == string || "".equals(string.trim())) {
            return null;
        }

        if (!string.contains(",")) {
            return new Integer[] { Integer.valueOf(string.trim()) };
        }

        String[] array = string.split(",");
        Integer[] ids = new Integer[array.length];
        int i = 0;
        try {
            for (String str : array) {
                ids[i++] = Integer.valueOf(str.trim());
            }
            return ids;
        } catch (NumberFormatException e) {
            throw e;
        }
    }

    public Long[] getParamToLongArray(String key) {
        String string = getParam(key);
        if (null == string || "".equals(string.trim())) {
            return null;
        }

        if (!string.contains(",")) {
            return new Long[] { Long.valueOf(string.trim()) };
        }

        String[] array = string.split(",");
        Long[] ids = new Long[array.length];
        int i = 0;
        try {
            for (String str : array) {
                ids[i++] = Long.valueOf(str.trim());
            }
            return ids;
        } catch (NumberFormatException e) {
            throw e;
        }
    }

    public List<Long> getParamToLongArrayList(String key){
        Long[] strings = getParamToLongArray(key);
        if(strings==null){
            return new ArrayList<>();
        }
        return new ArrayList<>(Arrays.asList(strings));
    }

    public BigInteger[] getParamToBigIntegerArray(String key) {
        String string = getParam(key);
        if (null == string || "".equals(string.trim())) {
            return null;
        }
        if (!string.contains(",")) {
            return new BigInteger[] { new BigInteger(string.trim()) };
        }
        String[] array = string.split(",");
        BigInteger[] ids = new BigInteger[array.length];
        int i = 0;
        try {
            for (String str : array) {
                ids[i++] = new BigInteger(str.trim());
            }
            return ids;
        } catch (NumberFormatException e) {
            throw e;
        }
    }

    public String[] getParamToStringArray(String key) {
        String string = getParam(key);
        if (null == string || "".equals(string.trim())) {
            return null;
        }
        if (!string.contains(",")) {
            return new String[] { string };
        }
        return string.split(",");
    }

    public List<String> getParamToStringArrayList(String key){
        String[] strings = getParamToStringArray(key);
        if(strings==null){
            return new ArrayList<>();
        }
        return new ArrayList<>(Arrays.asList(strings));
    }

    public Boolean getParamToBool(String key) {
        TemplateModel model = (TemplateModel) params.get(key);
        if (model == null) {
            return false;
        }
        try {
            if (model instanceof TemplateBooleanModel) {
                return ((TemplateBooleanModel) model).getAsBoolean();
            }
            if (model instanceof TemplateNumberModel) {
                return !(((TemplateNumberModel) model).getAsNumber().intValue() == 0);
            }
            if (model instanceof TemplateScalarModel) {
                String string = ((TemplateScalarModel) model).getAsString();
                if (null != string && !"".equals(string.trim())) {
                    return !(string.equals("0") || string.equalsIgnoreCase("false"));
                } else {
                    return false;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("param must is \"0\",\"1\"  or \"true\",\"false\"", e);
        }
        return false;
    }

    public Boolean getParamToBool(String key, Boolean defaultValue) {
        Boolean value = getParamToBool(key);
        if (value != null)
            return value;
        return defaultValue;
    }
}
